#! /usr/bin/env python
#coding=utf-8

"""

    2011 Boris Tatarintsev

    Python and PyGame implementation of well-known dotter game
    which is available for ios/android devices.

"""

import pygame
import pygame.locals
import sys
import os
import consts
import configreader
import time
import random
import copy
import tools
import menu
import particles
import strings
import eztext

from hsdrawer import HSDrawer
from hslocal import HSLocal
from math import sin, cos, pi


class Game(object):

    def __init__(self):
        
        self.mScreenFall = None
        
        # init config reader
        self.mCR = configreader.ConfigReader()
        # init pygame
        self.initPyGame(consts.SCREEN_WIDTH, consts.SCREEN_HEIGHT)
        # init sprites and menu
        self.initSprites()
        self.initMenu()
        # init high scores stuff
        self.mHSManager = HSLocal()        
        self.mHSManager.loadScoresData()
        self.mHSDrawer = HSDrawer(self.mHSManager)
        
    def initPyGame(self, scrWidth, scrHeight):
        # initialize game window
        pygame.init()
        # init display
        self.mScrWidth, self.mScrHeight = scrWidth, scrHeight
        self.mWindow = pygame.display.set_mode((self.mScrWidth, self.mScrHeight))
        self.mSurface = pygame.display.get_surface()
        # init fonts
        fontPath = os.path.join('..', 'font', 'freesansbold.ttf')
        self.bigfont = pygame.font.Font(fontPath, 40)
        self.medfont = pygame.font.Font(fontPath, 20)
        self.smallfont = pygame.font.Font(fontPath, 13)
        self.tinyfont = pygame.font.Font(fontPath, 12)
        self.microfont = pygame.font.Font(fontPath, 7)        
    
    def loadImagesList(self, imgList):
        output = {}
        idx = 0
        for item in imgList:
            output[idx] = pygame.image.load(item)
            idx += 1
        return output
    
    def initSprites(self):
        # load images
        self.mImgVBar = pygame.image.load(os.path.join('..', 'png', 'vert_bar.png'))        
        
        # load dots images        
        self.mImgDots = self.loadImagesList(tools.getFilesByMask(os.path.join('..', 'png'), 'dot*.png'))
        
        # cursors
        self.mImgSmallShadow = pygame.image.load(os.path.join('..', 'png', 'shadow_small.png'))
        self.mImgBigShadow = pygame.image.load(os.path.join('..', 'png', 'shadow_big.png'))
        self.mImgSmallShadow.set_alpha(50)
        self.mImgBigShadow.set_alpha(50)

        # load arrows images
        self.mArrowImgs = {}
        self.mArrowImgs[consts.DIRECT_LEFT] = pygame.image.load(os.path.join('..', 'png', 'arrow_left.png'))
        self.mArrowImgs[consts.DIRECT_RIGHT] = pygame.image.load(os.path.join('..', 'png', 'arrow_right.png'))
        self.mArrowImgs[consts.DIRECT_UP] = pygame.image.load(os.path.join('..', 'png', 'arrow_up.png'))
        self.mArrowImgs[consts.DIRECT_DOWN] = pygame.image.load(os.path.join('..', 'png', 'arrow_down.png'))
                        
        # scale all images down for hud
        self.mArrowImgsHUD = {}
        self.mArrowImgsHUD[consts.DIRECT_LEFT] = pygame.transform.scale(self.mArrowImgs[consts.DIRECT_LEFT], (20, 21))
        self.mArrowImgsHUD[consts.DIRECT_RIGHT] = pygame.transform.scale(self.mArrowImgs[consts.DIRECT_RIGHT], (20, 21))
        self.mArrowImgsHUD[consts.DIRECT_UP] = pygame.transform.scale(self.mArrowImgs[consts.DIRECT_UP], (20, 21))
        self.mArrowImgsHUD[consts.DIRECT_DOWN] = pygame.transform.scale(self.mArrowImgs[consts.DIRECT_DOWN], (20, 21))
        
        # load stars images
        self.mImgStarFull = pygame.image.load(os.path.join('..', 'png', 'star_full.png'))
        self.mImgStarEmpty = pygame.image.load(os.path.join('..', 'png', 'star_empty.png'))
        
        # load backgrounds
        self.mBacks = {}
        self.mBacks = self.loadImagesList(tools.getFilesByMask(os.path.join('..', 'png', 'bg'), 'bg*.png'))
        for j in range(len(self.mBacks)):
            self.mBacks[j].set_alpha(consts.BG_MAX_ALPHA)
        
        self.mDynObjsGroup = pygame.sprite.Group()

    def initMenu(self):
        # create main menu
        self.mMainMenu = menu.RotatingMenu(x = 320, y = 240, radius = 220, arc = pi, defaultAngle = pi / 2.0)
        self.mMainMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_STANDART))
        self.mMainMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_SURVIVE))
        self.mMainMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_HSCORE))
        self.mMainMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_EXIT))
        self.mMainMenu.selectItem(0)        
        # create score menu
        self.mScoreMenu = menu.RotatingMenu(x = 320, y = 240, radius = 200, arc = pi, defaultAngle = pi / 2.0)
        self.mScoreMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_STANDART))
        self.mScoreMenu.addItem(menu.MenuItem(strings.STR_MAIN_MENU_SURVIVE))
        self.mScoreMenu.addItem(menu.MenuItem(strings.STR_MENU_BACK))
        self.mScoreMenu.selectItem(0)        
        
    def startNewGame(self, gameType = strings.STR_MAIN_MENU_STANDART):
        
        self.mDynObjsGroup = pygame.sprite.Group()
        self.mParticlesGroup = pygame.sprite.Group()
        self.mTailsGroup = pygame.sprite.Group()
        particles.Particle.containers = self.mParticlesGroup
        
        self.mNameBox = eztext.Input(maxlength=6, color=(255,0,0), prompt='You\'ve got a highscore!', font=pygame.font.Font(None, 25), x = self.mScrWidth / 2, y = self.mScrHeight / 2)
        
        self.mNumDots = 0
        self.mLives = consts.MAX_LIVES
        self.mScore = 0
        self.mScoreTreshold = 0
        self.mGameType = gameType
        self.mSimulateBreakup = False
        self.mWasCollision = False
        
        # [timers for survival mode >]
        self.mTotalTime = consts.SURVIVAL_THINK_TIME_MS
        self.mTimeLeft = consts.SURVIVAL_THINK_TIME_MS
        # [<]
        
        self.mState = consts.STATE_PLAY        

        self.mBGChangeTime = pygame.time.get_ticks()
        self.mGameTime = pygame.time.get_ticks()
        
        self.mBackIdx = random.randint(0, len(self.mBacks) - 1)
        
        # make all backgrounds visible
        for bkgImg in self.mBacks.values():
            bkgImg.set_alpha(consts.BG_MAX_ALPHA)
        
        self.mNewBackIdx = self.mBackIdx        
        self.mNextSize = consts.SMALL_DOT_RADIUS
        self.mFlashScreen = False        
        self.mScreenFall = tools.getFallEffectDrawer(self.mSurface, self.mScrWidth, self.mScrHeight)
        
        self.mNextDot = self.mImgDots[random.randint(0, len(self.mImgDots) - 1)]
        self.mNextSize = consts.SMALL_DOT_RADIUS if self.mNextDot.get_rect().width / 2 == consts.SMALL_DOT_RADIUS else consts.BIG_DOT_RADIUS
        
        self.chooseRandomDirection()
        self.processEvents()

    def showMainMenu(self):
        self.mScreenFall = tools.getFallEffectDrawer(self.mSurface, self.mScrWidth, self.mScrHeight)
        self.mState = consts.STATE_MAIN_MENU
        self.mMainMenu.selectItem(0)
        self.processEvents()
        
    def showScoreTypeMenu(self):
        self.mScreenFall = tools.getFallEffectDrawer(self.mSurface, self.mScrWidth, self.mScrHeight)
        self.mState = consts.STATE_CHOOSE_SCORE_TYPE
        self.mScoreMenu.selectItem(0)
        self.processEvents()
        
    def getCurMenu(self):
        if self.mState == consts.STATE_MAIN_MENU:
            return self.mMainMenu
        elif self.mState == consts.STATE_CHOOSE_SCORE_TYPE :
            return self.mScoreMenu
            
    def showHSTable(self, scoreType = strings.STR_MAIN_MENU_STANDART):
        self.mScreenFall = tools.getFallEffectDrawer(self.mSurface, self.mScrWidth, self.mScrHeight)
        self.mHSDrawer.useTable(scoreType)
        self.mState = consts.STATE_SHOW_HIGH_SCORES

    def computeSpeed(self, dot, direction):
        if direction == consts.DIRECT_LEFT:            
            return -(1.0 * self.mScrWidth + dot.rect.width) / consts.DOT_TRAVEL_TIME_MS, 0
        elif direction == consts.DIRECT_RIGHT:
            return (1.0 * self.mScrWidth + dot.rect.width) / consts.DOT_TRAVEL_TIME_MS, 0
        elif direction == consts.DIRECT_UP:
            return 0, -(1.0 * self.mScrHeight +  dot.rect.height) / consts.DOT_TRAVEL_TIME_MS
        elif direction == consts.DIRECT_DOWN:
            return 0, (1.0 * self.mScrHeight +  dot.rect.height) / consts.DOT_TRAVEL_TIME_MS
    
    def reduceLives(self):
        if self.mLives > 0:
            self.mLives -= 1      
            self.mState = consts.STATE_PREPARE
            self.mShutterFunc = tools.getShutterEffectDrawer(self.mSurface, self.mImgVBar, self.mScrWidth, 2000)
            self.mFlashScreen = False
            self.mTimeLeft = self.mTotalTime
    
    def createNewDynamicObject(self, pos):        
        # creates a new moving dot
        newDot = pygame.sprite.Sprite(self.mDynObjsGroup)
        newDot.image = self.mNextDot
        newDot.rect = newDot.image.get_rect()
        newDot.rect.left, newDot.rect.top = pos[0] - newDot.rect.width / 2,\
                                            pos[1] - newDot.rect.height / 2

        newDot.coord_x, newDot.coord_y = newDot.rect.left, newDot.rect.top
        newDot.half_width = newDot.rect.width / 2
        newDot.half_height = newDot.rect.height / 2
        newDot.direction = self.mDirection
        newDot.collide = False            
        newDot.v_x, newDot.v_y = self.computeSpeed(newDot, newDot.direction)
        
        # particles have different mass for better reflection effects        
        newDot.mass = random.randint(1, 20) if newDot.half_width == consts.SMALL_DOT_RADIUS else random.randint(20, 40)            
        
        # choose next dot size
        self.mNextDot = self.mImgDots[random.randint(0, len(self.mImgDots) - 1)]
        self.mNextSize = consts.SMALL_DOT_RADIUS if self.mNextDot.get_rect().width / 2 == consts.SMALL_DOT_RADIUS else consts.BIG_DOT_RADIUS

        return newDot
        
    def chooseRandomDirection(self):
        self.mDirection = random.randint(0, consts.DIRECT_COUNT - 1)
        self.mAnimateDirection = True
        self.mDirectionAnimationTime = consts.DIRECT_ANIM_SCALE_TIME
    
    def testCollide(self, p1, p2):
        # test AABB - AABB intersection
        if tools.AABBAABB_Intersect(p1, p2):            
            # test circle-circle intersection
            return tools.circlesCollide((p1.coord_x, p1.coord_y), (p2.coord_x, p2.coord_y), p1.rect.width << 1, p2.rect.width << 1)
        return False
    
    def showExplode(self, p1, p2):
        # compute collision point
        a = (p1.coord_x - p2.coord_x, p1.coord_y - p2.coord_y)
        R = p2.half_width + p1.half_width
        b = (p2.coord_x + p2.coord_x / R, p2.coord_y + p2.coord_y / R)
        # show explosion :)
        for i in range(consts.COLLISION_PARTICLES):
            particles.Particle(b, random.uniform(-1, 1), random.uniform(-1, 1), 0, 0.01, 3,
                            [((random.randrange(128, 256), random.randrange(128, 256), random.randrange(128, 256)),
                                (255, 255, 255), 20), ((255, 255, 255), (0, 0, 0), 10)])
            
        
    def showMessage(self, in_str, centerX, centerY):
        # shows a rectangle with the text in it
        lbl = self.bigfont.render(in_str, 1, consts.COLOR_WHITE)
        width, height = self.bigfont.size(in_str) 
        width += 8
        pygame.draw.rect(self.mSurface, consts.COLOR_GRAY, pygame.Rect(centerX - width / 2, centerY - height / 2, width, height), 0)
        self.mSurface.blit(lbl, (centerX - width / 2 + 4, centerY - height / 2))
        
    def reflectCircles(self, c1, c2):
        new_v1, new_v2 = tools.getCirclesReflection( (c1.coord_x, c1.coord_y), (c1.v_x, c1.v_y), \
                                                     (c2.coord_x, c2.coord_y), (c2.v_x, c2.v_y) )
        
        c1.v_x, c1.v_y = new_v1[0], new_v1[1]
        c2.v_x, c2.v_y = new_v2[0], new_v2[1]
        
        c1.collide = True
        c2.collide = True
        
        # move circles a little bit away from each other to avoid glitches
        c1.coord_y += c1.v_y * 10
        c1.coord_x += c1.v_x * 10
        c2.coord_y += c2.v_y * 10
        c2.coord_x += c2.v_x * 10

    def processMenuAction(self, menuItemID):
        # what have been chosen?
        if self.mState == consts.STATE_MAIN_MENU:
            if menuItemID == strings.STR_MAIN_MENU_STANDART:
                self.startNewGame()
            elif menuItemID == strings.STR_MAIN_MENU_SURVIVE:
                self.startNewGame(strings.STR_MAIN_MENU_SURVIVE)
            elif menuItemID == strings.STR_MAIN_MENU_HSCORE:
                self.showScoreTypeMenu()
            elif menuItemID == strings.STR_MAIN_MENU_EXIT:
                sys.exit(0)
        elif self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
            if menuItemID == strings.STR_MAIN_MENU_STANDART:
                self.showHSTable(strings.STR_MAIN_MENU_STANDART)
            elif menuItemID == strings.STR_MAIN_MENU_SURVIVE:
                self.showHSTable(strings.STR_MAIN_MENU_SURVIVE)            
            elif menuItemID == strings.STR_MENU_BACK:
                self.showMainMenu()

    def processEvents(self):
        
        delta = 0        
        while True:
            
            s = pygame.time.get_ticks()
            events = pygame.event.get()
            
            for event in events:
                
                if event.type == pygame.locals.QUIT:
                    sys.exit(0)
                
                result = False
                if self.mState == consts.STATE_ENTER_NAME:
                    self.mNameBox.update(events)
                    
                elif self.mState == consts.STATE_SHOW_HIGH_SCORES:
                    result = self.mHSDrawer.processEvents(events, self)
                
                # if event was not handled elsewhere
                if not result:
                    
                    if event.type == pygame.locals.ACTIVEEVENT and (self.mState == consts.STATE_PLAY or self.mState == consts.STATE_PAUSED):
                        
                        if (event.state == 2 and event.gain == 0) or (event.state == 6 and event.gain == 0):
                            self.mShutterFunc = tools.getShutterEffectDrawer(self.mSurface, self.mImgVBar, self.mScrWidth, 2000)
                            self.mState = consts.STATE_PAUSED
                        elif(event.state == 6 and event.gain == 1):
                            self.mState = consts.STATE_PLAY                    
                    
                    if event.type == pygame.KEYDOWN:
                        
                        if self.mState == consts.STATE_MAIN_MENU or self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
                            if event.key == pygame.K_LEFT:
                                self.getCurMenu().selectItem(self.getCurMenu().selectedItemNumber + 1)
                            if event.key == pygame.K_RIGHT:
                                self.getCurMenu().selectItem(self.getCurMenu().selectedItemNumber - 1)
                            if event.key == pygame.K_RETURN:
                                self.processMenuAction(self.getCurMenu().getSelectedItemText())

                        if self.mState == consts.STATE_PLAY or self.mState == consts.STATE_CHOOSE_SCORE_TYPE or self.mState == consts.STATE_PAUSED \
                            or self.mState == consts.STATE_PREPARE or self.mState == consts.STATE_GAMEOVER:
                            if event.key == pygame.K_ESCAPE:
                                self.showMainMenu()
                                    
                        # cheat codes                        
                        elif event.key == 275 and self.mState == consts.STATE_PLAY:
                            self.mScore += 100
                        elif event.key == 276 and self.mState == consts.STATE_PLAY:
                            self.mLives -= 1
                    
                    elif event.type == pygame.MOUSEMOTION:
                        
                        if self.mState == consts.STATE_MAIN_MENU or self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
                            m_x, m_y = event.pos
                            for item in self.getCurMenu().items:
                                metrics = item.getMetrics()
                                if m_x >= metrics[0] and m_y >= metrics[1] and m_x <= metrics[2] and m_y <= metrics[3]:
                                    self.getCurMenu().selectedItem.deselect()
                                    self.getCurMenu().selectedItem = item
                                    item.select()
                                    break

                    elif event.type == pygame.MOUSEBUTTONDOWN and event.button == consts.LEFT_MOUSE_BUTTON:
                        
                        if self.mState == consts.STATE_MAIN_MENU or self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
                            # check if we clicked a mouse over any of menu items
                            m_x, m_y = event.pos
                            for item in self.getCurMenu().items:
                                metrics = item.getMetrics()
                                if m_x >= metrics[0] and m_y >= metrics[1] and m_x <= metrics[2] and m_y <= metrics[3]:
                                    self.processMenuAction(item.text)
                                    break
                        
                        elif self.mState == consts.STATE_PLAY:
                            
                            # okay, user has clicked inside the game field, create new moving sprite
                            # and start to animate it
                            if not self.mSimulateBreakup:
                                
                                newParticle = self.createNewDynamicObject(event.pos)
                                
                                # check if user doesn't try to click over another particle
                                success = True
                                for particle in self.mDynObjsGroup:
                                    if particle != newParticle:                                       
                                        if self.testCollide(particle, newParticle):
                                            self.mDynObjsGroup.remove(newParticle)
                                            success = False
                                            break
                                        
                                if success:
                                    self.mScore += consts.SCORE_STEP
                                    
                                    if self.mScore % 500 == 0 and self.mScore != 0 and self.mScore > self.mScoreTreshold:
                                        self.mTotalTime -= consts.SURVIVAL_TIME_REDUCE_MS
                                        self.mScoreTreshold = self.mScore
                                    
                                    self.chooseRandomDirection()
                                    self.mNumDots += 1
                                    self.mFlashScreen = False
                                    self.mTimeLeft = self.mTotalTime
                                    
                                    
                        elif self.mState == consts.STATE_PREPARE:
                            self.mState = consts.STATE_PLAY
                        
                        elif self.mState == consts.STATE_GAMEOVER:
                            if self.mHSManager.isHighScore(self.mGameType, self.mScore):
                                self.mState = consts.STATE_ENTER_NAME
                            else:
                                self.startNewGame(self.mGameType)                            
                            
            self.update(delta)
            self.paint(delta)
            delta = pygame.time.get_ticks() - s

    def update(self, deltaMS):
        
        # if deltaMS is very big this can cause the collision detection failure
        # so we just split the whole update onto the smaller time frames        
        if self.mState == consts.STATE_PLAY:
            
            self.mParticlesGroup.update(deltaMS)
            
            self.mGameTime += deltaMS
            oldDeltaMS = deltaMS            
            
            if self.mGameType == strings.STR_MAIN_MENU_SURVIVE:                
                if self.mTimeLeft <= 0:
                    self.mTimeLeft = self.mTotalTime
                    self.reduceLives()
                    self.mFlashScreen = False                                    
            
            # animate direction array if needed
            if self.mAnimateDirection:            
                self.mDirectionAnimationTime -= deltaMS
                if self.mDirectionAnimationTime <= 0:
                    # disable when animation is finished
                    self.mAnimateDirection = False
                        
            if deltaMS > consts.MAX_UPDATE_TIME_MS:
                totalSteps = deltaMS / consts.MAX_UPDATE_TIME_MS            
                deltaMS = consts.MAX_UPDATE_TIME_MS
            else:
                totalSteps = 1
            
            to_remove = set()
                
            for step in range(totalSteps):
                
                collidedParticles = False                
                
                # move all the sprites
                for dot in self.mDynObjsGroup:
                    
                    # where shall we go?
                    dot.coord_y += dot.v_y * deltaMS
                    dot.coord_x += dot.v_x * deltaMS
                    
                    # do we need to update the actual position?
                    dot.rect.top = dot.coord_y
                    dot.rect.left = dot.coord_x                                
                    
                    if not dot.collide:
                        
                        # check screen borders
                        if dot.coord_y <= -dot.rect.height and dot.direction == consts.DIRECT_UP:
                            dot.coord_y = self.mScrHeight + (dot.coord_y + dot.rect.height)
                        elif dot.coord_y >= self.mScrHeight and dot.direction == consts.DIRECT_DOWN:
                            dot.coord_y = -dot.rect.height + (dot.coord_y % self.mScrHeight)
                        elif dot.coord_x <= -dot.rect.width and dot.direction == consts.DIRECT_LEFT:
                            dot.coord_x = self.mScrWidth + (dot.coord_x + dot.rect.width)
                        elif dot.coord_x >= self.mScrWidth and dot.direction == consts.DIRECT_RIGHT:
                            dot.coord_x = -dot.rect.width + (dot.coord_x % self.mScrWidth)
                            
                    else:
                        
                        if dot not in to_remove:
                            collidedParticles = True
                            # if particle goes out of screen, we simply destroy it
                            if dot.coord_y <= -dot.rect.height or dot.coord_y >= self.mScrHeight or \
                               dot.coord_x <= -dot.rect.width or dot.coord_x >= self.mScrWidth:                                                                
                                to_remove.add(dot)
                                self.mScore -= consts.SCORE_STEP                        
                        
                    # check object AABBs collisions. it's fast and simple.
                    # most collisions will be refused at this step without testing circle-circle intersection
                    for dest in self.mDynObjsGroup:
                        if dot != dest:
                            if self.testCollide(dot, dest):                        
                                if not self.mSimulateBreakup:
                                    # start simple collisions simulation
                                    self.mSimulateBreakup = True
                                    self.mFlashScreen = True
                                    self.mColorIdx = 100                                    
                                    self.mWasCollision = True
                                    collidedParticles = True
                                    self.showExplode(dot, dest)
                                    self.reflectCircles(dot, dest)
                                else:
                                    # we are in breakup simulation mode, just compute circles collisions
                                    self.showExplode(dot, dest)
                                    self.reflectCircles(dot, dest)                                                                
                    
            if to_remove:
                for item in to_remove: self.mDynObjsGroup.remove(item)
                
            if not collidedParticles:
                
                self.mSimulateBreakup = False

                if self.mWasCollision:
                    self.reduceLives()
                    self.mWasCollision = False

                if self.mGameType == strings.STR_MAIN_MENU_SURVIVE:
                    if (100 * self.mTimeLeft) / self.mTotalTime < 20 and not self.mFlashScreen:
                        self.mColorIdx = 100
                        self.mFlashScreen = True
                                
                # don't count time during collision scene
                self.mTimeLeft -= oldDeltaMS
                
                # if player doesn't have lives left, go to game over state
                if self.mLives == 0:
                    self.mShutterFunc = tools.getShutterEffectDrawer(self.mSurface, self.mImgVBar, self.mScrWidth, 2000)
                    self.mState = consts.STATE_GAMEOVER
        
        elif self.mState == consts.STATE_MAIN_MENU:
            self.mMainMenu.update()
            
        elif self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
            self.mScoreMenu.update()
            
        elif self.mState == consts.STATE_ENTER_NAME:
            if self.mNameBox.finished:
                self.mHSManager.insertScore(self.mGameType, self.mNameBox.value, self.mScore)
                self.mHSManager.saveRawData()
                self.startNewGame()
    
    def drawHUD(self):
        
        # draw lives left
        x = consts.LIVES_HUD_X
        for j in range(consts.MAX_LIVES):
            tmpImg = self.mImgStarFull if j < self.mLives else self.mImgStarEmpty
            self.mSurface.blit(tmpImg, (x, consts.LIVES_HUD_Y))
            x += tmpImg.get_width()
        # draw last direction arrow
        x = self.mScrWidth - consts.DIRECT_HUD_X_OFFSET
        y = consts.DIRECT_HUD_Y
        tools.drawAligned(self.mSurface, self.mArrowImgsHUD[self.mDirection], x, y, tools.HORIZ_CENTER | tools.VERT_CENTER)        
        # draw score
        scrPosX = self.mScrWidth / 2
        tools.drawTextAligned(self.mSurface, self.smallfont, 'Score', scrPosX, 15, (255, 255, 255), tools.HORIZ_CENTER | tools.VERT_CENTER)
        tools.drawTextAligned(self.mSurface, self.smallfont, str(self.mScore), scrPosX, 30, (255, 255, 255), tools.HORIZ_CENTER | tools.VERT_CENTER)
        # draw game type string        
        tools.drawTextAligned(self.mSurface, self.smallfont, 'Game type: ' + self.mGameType, 0, self.mScrHeight, (100, 100, 100), tools.HORIZ_LEFT | tools.VERT_BOTTOM)
        # draw timeleft in survival mode
        if self.mGameType == strings.STR_MAIN_MENU_SURVIVE:
            tools.drawTextAligned(self.mSurface, self.medfont, str((self.mTimeLeft / 1000) + 1), self.mScrWidth, self.mScrHeight, (255, 255, 255), tools.HORIZ_RIGHT | tools.VERT_BOTTOM)
            
    def drawBackground(self):
        
        # draw grid
        for y in xrange(0, self.mScrHeight, 40):            
            pygame.draw.line(self.mSurface, (55, 55, 55), (0, y), (self.mScrWidth, y), 1)
        for x in xrange(0, self.mScrWidth, 40):
            pygame.draw.line(self.mSurface, (55, 55, 55), (x, 0), (x, self.mScrHeight), 1)
        
        if self.mState == consts.STATE_PLAY:
            time_passed = self.mGameTime - self.mBGChangeTime
            a = (consts.BG_CHANGE_TIME_MS - 2 * consts.BG_FADE_TIME_MS)
            if time_passed >= a and time_passed < a + consts.BG_FADE_TIME_MS:
                # start bg fade out transition
                p = consts.BG_MAX_ALPHA - (consts.BG_MAX_ALPHA * (time_passed - a)) / consts.BG_FADE_TIME_MS
                self.mBacks[self.mBackIdx].set_alpha(p)
                while self.mNewBackIdx == self.mBackIdx:
                    self.mNewBackIdx = random.randint(0, len(self.mBacks) - 1)
            elif time_passed >= consts.BG_CHANGE_TIME_MS - consts.BG_FADE_TIME_MS and time_passed < consts.BG_CHANGE_TIME_MS:
                # choose new random wallpaper
                p = (consts.BG_MAX_ALPHA * (time_passed - (consts.BG_CHANGE_TIME_MS - consts.BG_FADE_TIME_MS))) / consts.BG_FADE_TIME_MS
                self.mBackIdx = self.mNewBackIdx
                self.mBacks[self.mBackIdx].set_alpha(p)
            elif time_passed >= consts.BG_CHANGE_TIME_MS:
                self.mBGChangeTime = pygame.time.get_ticks()
                self.mBacks[self.mBackIdx].set_alpha(consts.BG_MAX_ALPHA)
            
        tools.drawAligned(self.mSurface, self.mBacks[self.mBackIdx], 0, 0, tools.HORIZ_LEFT | tools.VERT_TOP)            
        
    def paint(self, deltaMS):
        
        """
            Paint method
            
            It may seem strange that I pass deltaMS into it, but it is done
            for convenience. I tried not to use it straight in paint, passing
            it to another functions instead. Without this, my code would be twice as length
            and twice as hard to understand. :)
        """

        if self.mState == consts.STATE_PLAY or self.mState == consts.STATE_PAUSED or self.mState == consts.STATE_GAMEOVER \
            or self.mState == consts.STATE_PREPARE:
            
            if not self.mFlashScreen:
                self.mSurface.fill( (0, 0, 0) )
                self.drawBackground()            
            else:
                self.mSurface.fill( (self.mColorIdx, 0, 0, 10) )
                self.drawBackground()                        
                if self.mState == consts.STATE_PLAY:
                    self.mColorIdx -= 1
                if self.mColorIdx == 0: self.mColorIdx = 100

            self.mDynObjsGroup.draw(self.mSurface)
            self.mTailsGroup.draw(self.mSurface)

            if self.mAnimateDirection:
                
                # draw direction arrow if needed
                arrowImg = self.mArrowImgs[self.mDirection]            
                k = (1.0 * self.mDirectionAnimationTime / consts.DIRECT_ANIM_SCALE_TIME)
                k_x = arrowImg.get_width() - int(arrowImg.get_width() * k)
                k_y = arrowImg.get_height() - int(arrowImg.get_height() * k)
                tempImg = pygame.transform.scale(arrowImg, (k_x, k_y))
                tempImg.set_alpha(255 * k)
                tools.drawAligned(self.mSurface, tempImg, self.mScrWidth / 2, self.mScrHeight / 2, tools.HORIZ_CENTER | tools.VERT_CENTER)
            
            self.drawHUD()
            
            if self.mState == consts.STATE_PLAY:
                m_x, m_y = pygame.mouse.get_pos()
                shadow_img = self.mImgSmallShadow if self.mNextSize == consts.SMALL_DOT_RADIUS else self.mImgBigShadow
                tools.drawAligned(self.mSurface, shadow_img, m_x, m_y, tools.HORIZ_CENTER | tools.VERT_CENTER)
                self.mParticlesGroup.draw(self.mSurface)                
            elif self.mState == consts.STATE_PAUSED:
                self.mShutterFunc(deltaMS)                
                self.showMessage('Game paused', self.mScrWidth / 2, self.mScrHeight / 2)
            elif self.mState == consts.STATE_GAMEOVER:
                res = self.mShutterFunc(deltaMS)
                self.showMessage('Game over', self.mScrWidth / 2, self.mScrHeight / 2)
                if res: self.mState = consts.STATE_ENTER_NAME
            elif self.mState == consts.STATE_PREPARE:
                self.mShutterFunc(deltaMS)
                self.showMessage('Ready?', self.mScrWidth / 2, self.mScrHeight / 2)
            
        elif self.mState == consts.STATE_MAIN_MENU:
            
            self.mSurface.fill( (0, 0, 0) )
            self.mMainMenu.draw(self.mSurface)
            
        elif self.mState == consts.STATE_CHOOSE_SCORE_TYPE:
            
            self.mSurface.fill( (0, 0, 0) )
            self.mScoreMenu.draw(self.mSurface)            
            
        elif self.mState == consts.STATE_SHOW_HIGH_SCORES:
            
            self.mSurface.fill( (0, 0, 0) )
            self.mHSDrawer.draw(self.mSurface, self.mScrWidth, self.mScrHeight)
            
        elif self.mState == consts.STATE_ENTER_NAME:
            
            self.mNameBox.draw(self.mSurface)

        if self.mScreenFall != None:
            self.mScreenFall(deltaMS)
            
        
        pygame.display.flip()


pyDotterGame = Game()
pyDotterGame.showMainMenu()
